#include "Common.h"

Creature::Creature(ui64 guid) : m_sqlid(0), m_waypoint(0), m_lastwaypoint(0), ismoving(false),
m_lastmoveupdate(0), m_lastsrc(0, 0, 0), m_lastdest(0, 0, 0), m_wpsloaded(false), m_script(0),
m_creatureai(this), wpmin(0xFFFFFFFF), wpmax(0xFFFFFFFF), frm(0), tom(0)
{
	m_trainer = 0;
	m_guid.Init(guid);
	m_fieldcount = UNIT_END;
	m_updatemask.SetCount(UNIT_END);
	m_ui32values = _fields;
	memset(m_ui32values, NULL, UNIT_END * 4);
	SetUI64Value(OBJECT_FIELD_GUID, guid);
	SetUI32Value(OBJECT_FIELD_TYPE, 8 | 1);
	m_typeid = TYPEID_UNIT;
	SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, 0.0f);
	SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER, 0.0f);
	SetFloatValue(OBJECT_FIELD_SCALE_X, 1.0f);
	SetUI32Value(UNIT_FIELD_HEALTH, 1000);
	SetUI32Value(UNIT_FIELD_MAXHEALTH, 1000);
	SetUI32Value(UNIT_FIELD_BASE_HEALTH, 1000);
	SetUI32Value(UNIT_FIELD_POWER1, 0);
	SetUI32Value(UNIT_FIELD_MAXPOWER1, 0);
	SetUI32Value(UNIT_FIELD_BASE_MANA, 0);
	SetUI32Value(UNIT_FIELD_DISPLAYID, 100);
	SetUI32Value(UNIT_FIELD_NATIVEDISPLAYID, 100);
	SetUI32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
	SetUI32Value(UNIT_FIELD_LEVEL, 45);
	for(ui32 i = 0; i < 7; ++i)
		SetUI32Value(UNIT_FIELD_RESISTANCES + i, 0);
	SetUI32Value(UNIT_FIELD_BASEATTACKTIME, 1500);
	SetFloatValue(UNIT_FIELD_MINDAMAGE, 15);
	SetFloatValue(UNIT_FIELD_MAXDAMAGE, 20);
	SetUI32Value(UNIT_FIELD_FACTIONTEMPLATE, 35);
	SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 1.0f);
	SetFloatValue(UNIT_FIELD_COMBATREACH, 1.0f);
	SetUI32Value(UNIT_NPC_FLAGS, 1);
	SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
	SetByte( UNIT_FIELD_BYTES_0, 3, 0);
	SetUI32Value(UNIT_NPC_EMOTESTATE, 0);
	m_loaded = false;
}

void Creature::AddInRangeObject(Object* obj)
{
	ui32 prevsize = m_inrangeplayers.size();
	Object::AddInRangeObject(obj);
	if(obj->GetTypeId() == TYPEID_PLAYER && !prevsize)
	{
		UnitGoneActive();
	}
}

void Creature::UnitGoneActive()
{
	if(m_wpsloaded)
		return;
	_WayPointEntry* wpe = GetNextWaypoint();
	if(!wpe)
	{
		m_wpsloaded = true;
		return;
	}
	m_wpsloaded = true;
	ui32 nextwp = MoveTo(wpe->x, wpe->y, wpe->z);
	TimedEvent* te = new TimedEvent(new CallbackP0<Creature>(&Creature::WaypointCallback, this), nextwp ? nextwp : 2000, 1, 200);
	AddEvent(te);
}

void Creature::WaypointCallback()
{
	_WayPointEntry* wpe = GetNextWaypoint();
	if(!wpe)
		return;
	ui32 nextwp = MoveTo(wpe->x, wpe->y, wpe->z);
	TimedEvent* te = new TimedEvent(new CallbackP0<Creature>(&Creature::WaypointCallback, this), nextwp ? nextwp : 2000, 1, 200);
	AddEvent(te);
}

_WayPointEntry* Creature::GetNextWaypoint()
{
	if(!m_waypoint)
		return 0;

	if(!frm || !tom)
	{
		unordered_set<_WayPointEntry*>::iterator itr = m_waypoint->waypoints.begin();
		unordered_set<_WayPointEntry*>::iterator end = m_waypoint->waypoints.end();

		for( ; itr != end; ++itr)
		{
			if(wpmin == 0xFFFFFFFF)
			{
				frm = (*itr);
				wpmin = (*itr)->index;
			}
			else if(wpmin > (*itr)->index)
			{
				frm = (*itr);
				wpmin = (*itr)->index;
			}
			if(wpmax == 0xFFFFFFFF)
			{
				tom = (*itr);
				wpmax =(*itr)->index;
			}
			else if(wpmax < (*itr)->index)
			{
				tom = (*itr);
				wpmax = (*itr)->index;
			}
			m_waypoints[(*itr)->index] = *itr;
		}
	}

	if(m_lastwaypoint == wpmax)
	{
		m_lastwaypoint = wpmin;
		return frm;
	}

	_WayPointEntry* tmp = m_waypoints[m_lastwaypoint];
	stdext::hash_map<ui32, _WayPointEntry*>::iterator itr = m_waypoints.find(m_lastwaypoint);
	if(itr == m_waypoints.end())
		return 0;
	stdext::hash_map<ui32, _WayPointEntry*>::iterator i2 = ++itr;
	if(i2 == m_waypoints.end())
		return 0;
	return i2->second;
	/*for( ; itr != end; ++itr)
	{
		if(!tmp && (*itr)->index > m_lastwaypoint)
			tmp = (*itr);
		if((*itr)->index > m_lastwaypoint && tmp->index > (*itr)->index)
			tmp = (*itr);
	}
	if(tmp)
		m_lastwaypoint = tmp->index;

	return tmp;*/
}

void Creature::Spawn(float x, float y, float z, ui32 map, float o)
{
	if(!m_loaded)
		return;
	SetPosition(x, y, z, map, o);
	if(!m_maphandler)
		m_maphandler = sWorld.GetMapHandler(map);
	if(!m_maphandler)
		return;
	SetEventWorker(m_maphandler);
	m_maphandler->AddObject(this);
	m_taxinode = sTaxiHandler.GetNextNode(x, y, z, map);
	m_trainer = miscmgr->GetTrainer(GetEntry());
	m_guardpoi = miscmgr->GetGuardPoi(GetEntry());
	m_waypoint = miscmgr->GetWayPoints(m_sqlid);
}

void Creature::Load(ui32 entry)
{
	SetUI32Value(OBJECT_FIELD_ENTRY, entry);
	CreatureBasic* cb = miscmgr->GetCreatureBasic(entry);
	if(!cb)
		return;
	SetUI32Value(UNIT_FIELD_DISPLAYID, cb->displayid1);
	SetUI32Value(UNIT_FIELD_NATIVEDISPLAYID, cb->displayid1);
	CreatureDetails* det = miscmgr->GetCreatureDetails(entry);
	if(!det)
		return;
	SetUI32Value(UNIT_NPC_FLAGS, det->npcflags);
	ui32 mini = min(det->minlvl, det->maxlvl);
	ui32 maxi = max(det->minlvl, det->maxlvl);
	if(mini != maxi)
		mini += rand() % (maxi - mini);
	SetUI32Value(UNIT_FIELD_LEVEL, mini);
	mini = min(det->minhealth, det->maxhealth);
	maxi = min(det->minhealth, det->maxhealth);
	if(mini != maxi)
		mini += rand() % (maxi - mini);
	SetUI32Value(UNIT_FIELD_BASE_HEALTH, mini);
	SetUI32Value(UNIT_FIELD_HEALTH, mini);
	SetUI32Value(UNIT_FIELD_MAXHEALTH, mini);
	SetFloatValue(OBJECT_FIELD_SCALE_X, det->scale);
	SetUI32Value(UNIT_FIELD_FACTIONTEMPLATE, det->faction);
	SetUI32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, det->mainhand);
	SetUI32Value(UNIT_VIRTUAL_ITEM_SLOT_ID_1, det->offhand);
	SetUI32Value(UNIT_VIRTUAL_ITEM_SLOT_ID_2, det->rangedhand);
	m_gscript = sScriptMgr->GetScriptForCreature(entry);
	m_script = sScriptMgr->GetNewScriptForCreature(this);
	m_loaded = true;
	UnitGoneActive();
}

void Creature::SaveToDb(bool first)
{
	stringstream strm;
	strm << "Replace into creature_spawns VALUES (";
	if(first && !m_sqlid)
	{
		strm << "NULL, ";
	}
	else if(!first && m_sqlid)
		strm << m_sqlid << " , ";
	strm << GetUI32Value(OBJECT_FIELD_ENTRY) << " , ";
	strm << m_position._x << " , " << m_position._y << " , " << m_position._z << " , " << m_position._mapid << " , ";
	strm << m_orientation << " , " << " , " << GetUI32Value(UNIT_FIELD_FACTIONTEMPLATE) << " , ";
	strm << GetUI32Value(UNIT_VIRTUAL_ITEM_SLOT_ID) << " , ";
	strm << GetUI32Value(UNIT_VIRTUAL_ITEM_SLOT_ID_1) << " , " << GetUI32Value(UNIT_VIRTUAL_ITEM_SLOT_ID_2) << " , ";
	strm << GetUI32Value(UNIT_NPC_EMOTESTATE) << ")";
	CharacterDatabase.Query(strm.str().c_str());
	if(first)
	{
		QResult* res = CharacterDatabase.SafeQuery("Select LAST_INSERT_ID() from creature_spawns");
		if(!res || !res->NumRows())
			return;
		ui32 id = res->Fetch()[0];
		m_sqlid = id;
	}
}

bool Creature::HasFlag(ui32 flag)
{
	ui32 flags = GetUI32Value(UNIT_NPC_FLAGS);
	return (flags & flag) != 0;
}

bool Creature::IsBattlemaster()
{
	return HasFlag(NPC_FLAG_BATTLEFIELDPERSON);
}

bool Creature::IsInnkeeper()
{
	return HasFlag(NPC_FLAG_INNKEEPER);
}

bool Creature::IsGossipNpc()
{
	return HasFlag(NPC_FLAG_GOSSIP);
}

bool Creature::IsQuestGiver()
{
	return HasFlag(NPC_FLAG_QUESTGIVER);
}

bool Creature::IsTrainer()
{
	return (HasFlag(NPC_FLAG_TRAINER) || HasFlag(NPC_FLAG_TRAINER_PROF));
}

bool Creature::IsTaxiVendor()
{
	return HasFlag(NPC_FLAG_TAXIVENDOR);
}

bool Creature::IsGuard()
{
	return HasFlag(NPC_FLAG_GOSSIP) && (m_guardpoi != 0);
}

void Creature::SendTaxiList(Player* requester)
{
	if(!m_taxinode)
	{
		Log->Notice("TaxiHandler", "NPC '%u' (Position x: %f y: %f z:%f map: %u) is not linked to a taxinode", GetUI32Value(OBJECT_FIELD_ENTRY),
			m_position._x, m_position._y, m_position._z, m_position._mapid);
		requester->GossipEnd();
		return;
	}
	ui32 pid = m_taxinode->id;
	ui8 pos = (pid - 1) / 32;
	ui32 mask = 1 << ((pid - 1) % 32);
	ui32 pmk;
	if(!requester->GetTaxiStatus(pos, pmk))
	{
		Log->Error("TaxiHandler", "Could not find taxistatus for Player '%s'. Reason: ((pathid - 1) / 32) > 11!",
			requester->GetName());
		return;
	}
	if(!(pmk & mask))
	{
		requester->SetTaxiStatus(pos, mask | pmk);

		RealmPacket data(SMSG_NEW_TAXI_PATH);
		requester->GetSession()->SendPacket(&data);

		RealmPacket update(SMSG_TAXINODE_STATUS);
		update << GetGUID() << ui8(1);
		requester->GetSession()->SendPacket(&update);
	}
	ui32 stat;
	requester->GetTaxiStatus(pos, stat);
	ui32 taximask[12];
	sTaxiHandler.SetTaximask(taximask);
	taximask[pos] |= 1 << ((pid - 1) % 32);
	for(int i = 0; i < 12; ++i)
	{
		ui32 status;
		requester->GetTaxiStatus(i, status);
		taximask[i] &= status;
	}
	RealmPacket data(SMSG_SHOWTAXINODES);
	data << ui32(1) << requester->GetGUID();
	data << pid;
	for(int i = 0; i < 12; ++i)
		data << taximask[i];

	requester->GetSession()->SendPacket(&data);
}

void Creature::SendNullSpell(Player* requester)
{
	RealmPacket data(SMSG_TRAINER_LIST);
	data << GetGUID() << ui32(0) << ui32(0);
	data << "Hallo!";
	requester->GetSession()->SendPacket(&data);
}

void Creature::SendSpellList(Player* requester)
{
	if(!m_trainer)
	{
		SendNullSpell(requester);
		return;
	}
	if(!m_trainer->m_trainerspells.size())
	{
		SendNullSpell(requester);
		return;
	}
	set<TrainerSpell*>::iterator itr = m_trainer->m_trainerspells.begin();
	set<TrainerSpell*>::iterator end = m_trainer->m_trainerspells.end();

	RealmPacket data(SMSG_TRAINER_LIST);
	data << GetGUID() << ui32(0) << ui32(m_trainer->m_trainerspells.size());
	for( ; itr != end; ++itr)
	{
		ui8 status = 0;
		if(requester->HasSpell((*itr)->id))
			status = 2;
		else if(requester->GetUI32Value(UNIT_FIELD_LEVEL) < (*itr)->level)
			status = 1;
		else if(requester->GetUI32Value(PLAYER_FIELD_COINAGE) < (*itr)->price)
			status = 1;
		data << (*itr)->id;
		data << status;
		data << (*itr)->price;
		data << ui32(0);
		data << ui32(0);
		data << ui8((*itr)->level);
		data << (*itr)->required_skill;
		data << (*itr)->required_skill_rank;
		data << (*itr)->required_spell;
		data << ui32(0);
		data << ui32(0);
	}
	data << "Hallo!";
	requester->GetSession()->SendPacket(&data);
}

void Creature::RemoveFromWorld()
{
	if(m_maphandler)
		m_maphandler->RemoveObject(this);
}

void Creature::RemoveFromDb()
{
	CharacterDatabase.Query("Delete from creature_spawns where `index` = %u", m_sqlid);
}

ui32 Creature::MoveTo(float x, float y, float z, bool run)
{
	float distance = Useable::GetDistance(this, x, y, z);
	if(distance < 2.0f)
		return 0;
	ui32 time = 0;
	if(run)
		time = ui32(distance / 7.5f) * 1000;
	else
		time = ui32(distance / 2.5f) * 1000;
	RealmPacket data(SMSG_MONSTER_MOVE);
	data << m_guid;
	data << ui8(0);
	data << m_position._x << m_position._y << m_position._z;
	data << ui32(GetTickCount()) << ui8(0);
	if(!run)
		data << ui32(0x000);
	else
		data << ui32(0x100);
	data << time << ui32(1);
	data << x << y << z;
	SendToSet(data);
	ismoving = true;
	m_lastsrc = Vector3d(m_position._x, m_position._y, m_position._z);
	m_lastdest = Vector3d(x, y, z);
	m_lastmovement = GetTickCount();
	m_timetodest = time;
	PushToWorker();
	return time;
}

void Creature::StopMovement()
{
	ismoving = false;
	RealmPacket data(SMSG_MONSTER_MOVE);
	data << m_guid << ui8(0) << m_position._x << m_position._y << m_position._z;
	data << ui32(GetTickCount()) << ui8(1);
	SendToSet(data);
}

void Creature::UpdateMovements()
{
	if(!ismoving)
		return;
	if(GetTickCount() - m_lastmoveupdate < 500)
		return;
	m_lastmoveupdate = GetTickCount();
	Vector3d direction = m_lastdest - m_lastsrc;
	float pct = ((float)m_lastmoveupdate - m_lastmovement) / ((float)m_timetodest);
	if(abs(1.0f - pct) <= 1e-2)
	{
		ismoving = false;
		return;
	}
	direction = direction * pct;
	direction = m_lastsrc + direction;
	SetPosition(direction.X, direction.Y, direction.Z);
}

bool Creature::Update(ui32 diff)
{
	UpdateMovements();
	return EventHolder::Update(diff) || ismoving;
}

void Creature::StopWaypointing(ui32 time)
{
	if(!m_waypoint)
		return;
	if(time)
	{
		TimedEvent* te = new TimedEvent(new CallbackP0<Creature>(&Creature::RestartWaypointing, this), time, 1);
		AddEvent(te);
	}
	ismoving = false;
	RemoveEventsByType(200);	
}

void Creature::RestartWaypointing()
{
	if(!m_waypoint)
		return;
	_WayPointEntry* wpe = 0;
	unordered_set<_WayPointEntry*>::iterator itr = m_waypoint->waypoints.begin();
	unordered_set<_WayPointEntry*>::iterator end = m_waypoint->waypoints.end();
	for( ; itr != end; ++itr)
	{
		if((*itr)->index == m_lastwaypoint)
		{
			wpe = (*itr);
			break;
		}
	}

	if(!wpe)
		return;

	ui32 time = MoveTo(wpe->x, wpe->y, wpe->z);
	TimedEvent* te = new TimedEvent(new CallbackP0<Creature>(&Creature::WaypointCallback, this), time ? time : 2000, 1, 200);
	AddEvent(te);
}

void Creature::PlayerLoaded(Player* plr)
{
	if(ismoving && (m_lastdest.X != 0.0 || m_lastdest.Y != 0.0 || m_lastdest.Z != 0.0) && m_timetodest && m_lastmovement)
	{
		plr->ProcessUpdates();
		RealmPacket data(SMSG_MONSTER_MOVE);
		data << m_guid << ui8(0) << m_position._x << m_position._y << m_position._z;
		data << ui32(GetTickCount()) << ui8(0) << ui32(0x000) << ui32(m_timetodest - (GetTickCount() - m_lastmovement)) << ui32(1);
		data << (float)m_lastdest.X << (float)m_lastdest.Y << (float)m_lastdest.Z;
		plr->GetSession()->SendPacket(&data);
	}
}

void Creature::OnPushToWorld()
{
	//UnitGoneActive();
}